﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace BoxGame.World
{
    class Map
    {
        private int sizeX;
        private int sizeY;
        private int sizeZ;
        //private Tile[,,] tiles;
        private int[,] boxPlan;
        private int[,] fillPlan;
        private int[,] shortPlan;
        private VertexPositionColorTexture[,][] verticesList;
        private int exitX;
        private int exitY;
        private int exitZ;

        public int SizeX
        {
            get
            {
                return sizeX;
            }
        }

        public int SizeY
        {
            get
            {
                return sizeY;
            }
        }

        public int SizeZ
        {
            get
            {
                return sizeZ;
            }
        }

        public int ExitX
        {
            get
            {
                return exitX;
            }
        }

        public int ExitY
        {
            get
            {
                return exitY;
            }
        }

        public int ExitZ
        {
            get
            {
                return exitZ;
            }
        }

        public Map(int orgX, int orgY, int orgZ)
        {
            sizeX = orgX;
            sizeY = orgY;
            sizeZ = orgZ;
            double zahl;

            boxPlan = new int[sizeX, sizeZ];
            fillPlan = new int[sizeX, sizeZ];
            shortPlan = new int[sizeX, sizeZ];

            verticesList = new VertexPositionColorTexture[sizeX, sizeZ][];

            Random random = new Random();
            exitX = random.Next(sizeX);
            exitZ = random.Next(sizeZ);

            do
            {
                for (int x = 0; x < sizeX; x++)
                {
                    for (int z = 0; z < sizeZ; z++)
                    {
                        boxPlan[x, z] = 0;
                        fillPlan[x, z] = 0;
                        shortPlan[x, z] = 0;
                        zahl = random.NextDouble();
                        if (zahl > 0.7f)
                        {
                            boxPlan[x, z] = 1;
                        }

                        if (x == exitX && z == exitZ)
                        {
                            boxPlan[x, z] = 2;
                        }

                        if (x == 0)
                        {
                            boxPlan[x, z] = 1;
                        }

                        if (x == sizeX - 1)
                        {
                            boxPlan[x, z] = 1;
                        }

                        if (z == 0)
                        {
                            boxPlan[x, z] = 1;
                        }

                        if (z == sizeZ - 1)
                        {
                            boxPlan[x, z] = 1;
                        }

                        boxPlan[1, 1] = 0;
                    }
                }
            } while (!IsExitReachableFrom(1,1));

            float textureX = 9f;
            float textureY = 10f;
            float posX = 1f;
            float posY = 0f; 

            Vector2 topLeft = new Vector2((posX + 0f)/ textureX, (posY + 0f) / textureY);
            Vector2 bottomLeft = new Vector2((posX + 0f) / textureX, (posY + 1f) / textureY);
            Vector2 topRight = new Vector2((posX + 1f) / textureX, (posY + 0f) / textureY);
            Vector2 bottomRight = new Vector2((posX + 1f) / textureX, (posY + 1f) / textureY);

            for (int x = 0; x < sizeX; x++)
            {
                for (int z = 0; z < sizeZ; z++)
                {

                    if (boxPlan[x, z] == 0)
                    {
                        //floor or ceiling
                        verticesList[x, z] = new VertexPositionColorTexture[6];
                        verticesList[x, z][0] = new VertexPositionColorTexture(new Vector3(x, 0, z), Color.Red, topLeft);
                        verticesList[x, z][1] = new VertexPositionColorTexture(new Vector3(x, 0, z + 1), Color.Red, topRight);
                        verticesList[x, z][2] = new VertexPositionColorTexture(new Vector3(x + 1, 0, z), Color.Red, bottomLeft);

                        verticesList[x, z][3] = new VertexPositionColorTexture(new Vector3(x, 0, z + 1), Color.Red, topRight);
                        verticesList[x, z][4] = new VertexPositionColorTexture(new Vector3(x + 1, 0, z + 1), Color.Red, bottomRight);
                        verticesList[x, z][5] = new VertexPositionColorTexture(new Vector3(x + 1, 0, z), Color.Red, bottomLeft);
                    }
                    else
                    {
                        //floor or ceiling
                        verticesList[x, z] = new VertexPositionColorTexture[30];
                        //ceiling
                        verticesList[x, z][0] = new VertexPositionColorTexture(new Vector3(x, 1, z), Color.Yellow, topLeft);
                        verticesList[x, z][1] = new VertexPositionColorTexture(new Vector3(x, 1, z + 1), Color.Yellow, topRight);
                        verticesList[x, z][2] = new VertexPositionColorTexture(new Vector3(x + 1, 1, z), Color.Yellow, bottomLeft);

                        verticesList[x, z][3] = new VertexPositionColorTexture(new Vector3(x, 1, z + 1), Color.Yellow, topRight);
                        verticesList[x, z][4] = new VertexPositionColorTexture(new Vector3(x + 1, 1, z + 1), Color.Yellow, bottomRight);
                        verticesList[x, z][5] = new VertexPositionColorTexture(new Vector3(x + 1, 1, z), Color.Yellow, bottomLeft);

                        //front wall
                        verticesList[x, z][6] = new VertexPositionColorTexture(new Vector3(x + 1, 0, z + 1), Color.Green, bottomRight);
                        verticesList[x, z][7] = new VertexPositionColorTexture(new Vector3(x, 1, z + 1), Color.Green, topLeft);
                        verticesList[x, z][8] = new VertexPositionColorTexture(new Vector3(x, 0, z + 1), Color.Green, bottomLeft);

                        verticesList[x, z][9] = new VertexPositionColorTexture(new Vector3(x, 1, z + 1), Color.Green,topLeft);
                        verticesList[x, z][10] = new VertexPositionColorTexture(new Vector3(x + 1, 0, z + 1), Color.Green, bottomRight);
                        verticesList[x, z][11] = new VertexPositionColorTexture(new Vector3(x + 1, 1, z + 1), Color.Green, topRight);

                        //back wall
                        verticesList[x, z][12] = new VertexPositionColorTexture(new Vector3(x + 1, 0, z), Color.Blue, bottomLeft);
                        verticesList[x, z][13] = new VertexPositionColorTexture(new Vector3(x, 0, z), Color.Blue, bottomRight);
                        verticesList[x, z][14] = new VertexPositionColorTexture(new Vector3(x, 1, z), Color.Blue, topRight);

                        verticesList[x, z][15] = new VertexPositionColorTexture(new Vector3(x, 1, z), Color.Blue, topRight);
                        verticesList[x, z][16] = new VertexPositionColorTexture(new Vector3(x + 1, 1, z), Color.Blue, topLeft);
                        verticesList[x, z][17] = new VertexPositionColorTexture(new Vector3(x + 1, 0, z), Color.Blue, bottomLeft);

                        //left wall
                        verticesList[x, z][18] = new VertexPositionColorTexture(new Vector3(x, 0, z), Color.White, bottomLeft);
                        verticesList[x, z][19] = new VertexPositionColorTexture(new Vector3(x, 0, z + 1), Color.White, bottomRight);
                        verticesList[x, z][20] = new VertexPositionColorTexture(new Vector3(x, 1, z + 1), Color.White, topRight);

                        verticesList[x, z][21] = new VertexPositionColorTexture(new Vector3(x, 1, z + 1), Color.White, topRight);
                        verticesList[x, z][22] = new VertexPositionColorTexture(new Vector3(x, 1, z), Color.White, topLeft);
                        verticesList[x, z][23] = new VertexPositionColorTexture(new Vector3(x, 0, z), Color.White, bottomLeft);

                        //right wall
                        verticesList[x, z][24] = new VertexPositionColorTexture(new Vector3(x + 1, 0, z), Color.Violet, bottomRight);
                        verticesList[x, z][25] = new VertexPositionColorTexture(new Vector3(x + 1, 1, z + 1), Color.Violet, topLeft);
                        verticesList[x, z][26] = new VertexPositionColorTexture(new Vector3(x + 1, 0, z + 1), Color.Violet, bottomLeft);

                        verticesList[x, z][27] = new VertexPositionColorTexture(new Vector3(x + 1, 1, z + 1), Color.Violet, topLeft);
                        verticesList[x, z][28] = new VertexPositionColorTexture(new Vector3(x + 1, 0, z), Color.Violet, bottomRight);
                        verticesList[x, z][29] = new VertexPositionColorTexture(new Vector3(x + 1, 1, z), Color.Violet, topRight);
                    }
                }
            }
        }

        public void DrawMap(GraphicsDevice graphicsDevice, Vector3 cameraPosition)
        {
            float alpha = 0.0f;
            for (int x = (int)cameraPosition.X - 20; x < (int)cameraPosition.X + 20; x++)
            {
                if (x < 0)
                {
                    continue;
                }
                if (x >= sizeX)
                {
                    continue;
                }
                for (int z = (int)cameraPosition.Z - 20; z < (int)cameraPosition.Z + 20; z++)
                {
                    if (z < 0)
                    {
                        continue;
                    }
                    if (z >= SizeZ)
                    {
                        continue;
                    }
                    alpha = 1.00f - (Math.Abs(x - cameraPosition.X + 0.5f) + Math.Abs(z - cameraPosition.Z + 0.5f)) * 0.1f;
                    if (boxPlan[x, z] == 0)
                    {
                        for (int v = 0; v < 6; v++)
                        {
                            verticesList[x, z][v].Color = Color.Lerp(Color.Black, Color.BurlyWood, alpha);
                        }
                        graphicsDevice.DrawUserPrimitives<VertexPositionColorTexture>(PrimitiveType.TriangleList, verticesList[x, z], 0, 2);

                    }
                    else
                    {
                        if (boxPlan[x, z] == 1)
                        {
                            for (int v = 0; v < 30; v++)
                            {
                                verticesList[x, z][v].Color = Color.Lerp(Color.Black, Color.Gray, alpha);
                            }
                            graphicsDevice.DrawUserPrimitives<VertexPositionColorTexture>(PrimitiveType.TriangleList, verticesList[x, z], 0, 10);
                        }
                        else
                        {
                            if (boxPlan[x, z] == 2)
                            {
                                for (int v = 0; v < 30; v++)
                                {
                                    verticesList[x, z][v].Color = Color.Lerp(Color.Black, Color.Yellow, alpha);
                                }
                                graphicsDevice.DrawUserPrimitives<VertexPositionColorTexture>(PrimitiveType.TriangleList, verticesList[x, z], 0, 10);
                            }

                        }
                    }
                }
            }
        }

        public bool IsWalkable(Vector3 position)
        {
            if (position.X < 0)
                return false;
            if (position.X > sizeX - 1)
                return false;
            if (position.Z < 0)
                return false;
            if (position.Z > sizeZ - 1)
                return false;

            if (boxPlan[(int)Math.Floor(position.X + 0.1f), (int)Math.Floor(position.Z)] == 0)
            {
                if (boxPlan[(int)Math.Floor(position.X - 0.1f), (int)Math.Floor(position.Z)] == 0)
                {
                    if (boxPlan[(int)Math.Floor(position.X), (int)Math.Floor(position.Z + 0.1f)] == 0)
                    {
                        if (boxPlan[(int)Math.Floor(position.X), (int)Math.Floor(position.Z - 0.1f)] == 0)
                        {
                            return true;
                        }
                        else
                        {
                            return false;
                        }
                    }
                    else
                    {
                        return false;
                    }
                }
                else
                {
                    return false;
                }
            }
            else
            {
                return false;
            }  
        }

        private bool IsExitReachableFrom(int x, int z)
        {
            Stack<Point> pointStack = new Stack<Point>();
            pointStack.Push(new Point(x, z));

            while (pointStack.Count > 0)
            {
                Point p = pointStack.Pop();
                if (boxPlan[p.X, p.Y] == 0 || boxPlan[p.X, p.Y] == 2)
                {
                    if (fillPlan[p.X, p.Y] == 0)
                    {
                        fillPlan[p.X, p.Y] = 1;
                        pointStack.Push(new Point(p.X + 1, p.Y));
                        pointStack.Push(new Point(p.X - 1, p.Y));
                        pointStack.Push(new Point(p.X, p.Y + 1));
                        pointStack.Push(new Point(p.X, p.Y - 1));
                    }
                }
            }

            return (fillPlan[exitX, exitZ] == 1);
        }

        private int shortestPathFrom(int x, int z)
        {
            Stack<Point> openStack = new Stack<Point>();


            return 0;
        }
    }
}
